package com.yckj.activechicken.business.common.controller;

import com.yckj.activechicken.business.common.model.ApproverInfo;
import com.yckj.activechicken.business.common.model.vo.ApproverInfoVo;
import com.yckj.activechicken.business.common.model.vo.HistoricActivity;
import com.yckj.activechicken.business.common.model.vo.ProcessDefinitionVo;
import com.yckj.activechicken.business.common.service.IApproverService;
import com.yckj.activechicken.business.common.service.IProcessService;
import com.yckj.activechicken.common.constant.ProcessContants;
import com.yckj.activechicken.common.utils.StringUtils;
import com.yckj.activechicken.framework.web.controller.BaseController;
import com.yckj.activechicken.framework.web.domain.AjaxResult;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * <p>
 * Title:com.yckj.activechicken.business.common.controller
 * </p>
 * <p/>
 * <p>
 * Description: 描述【流程管理通用功能控制器】
 * </p>
 * <p/>
 * <p>
 * Copyright: Copyright (c) 2019
 * </p>
 * <p/>
 * <p>
 * Company: 英才汇硕信息技术有限公司
 * </p>
 *
 * @author zhoujy
 * @version 1.0
 * @created 2020/5/27 9:03
 */
@RestController
@RequestMapping("/process/general")
public class ProcessController extends BaseController {

    @Autowired
    private IProcessService processService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private IApproverService approverService;


    /**
     * 查询流程定义
     * @param processDefinitionVo
     * @return
     */
    @GetMapping("/listDefinition")
    public AjaxResult listDefinition(ProcessDefinitionVo processDefinitionVo) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        processDefinitionQuery.latestVersion();
        if (StringUtils.isNotBlank(processDefinitionVo.getName())) {
            processDefinitionQuery.processDefinitionNameLike("%" + processDefinitionVo.getName() + "%");
        }
        if (StringUtils.isNotBlank(processDefinitionVo.getKey())) {
            processDefinitionQuery.processDefinitionKeyLike("%" + processDefinitionVo.getKey() + "%");
        }

        List<ProcessDefinition> list = processDefinitionQuery.list();

        List<ProcessDefinitionVo> processDefinitionVoList = new ArrayList<>();
        if(StringUtils.isNotEmpty(list)){
            for(ProcessDefinition processDefinition : list){
                ProcessDefinitionVo outProcessDefinition = new ProcessDefinitionVo();
                outProcessDefinition.setId(processDefinition.getId());
                outProcessDefinition.setName(processDefinition.getName());
                outProcessDefinition.setKey(processDefinition.getKey());
                outProcessDefinition.setVersion(processDefinition.getVersion());
                outProcessDefinition.setDescription(processDefinition.getDescription());
                outProcessDefinition.setDeploymentId(processDefinition.getDeploymentId());

                Deployment deployment = repositoryService.createDeploymentQuery()
                        .deploymentId(processDefinition.getDeploymentId())
                        .singleResult();
                outProcessDefinition.setDeploymentTime(deployment.getDeploymentTime());
                processDefinitionVoList.add(outProcessDefinition);
            }
        }

        return AjaxResult.success(processDefinitionVoList);
    }

    /**
     * 查询任务节点
     * @param processDefinitionId
     * @return
     */
    @GetMapping("/listUserTask")
    public AjaxResult listUserTask(String processDefinitionId) {
        BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        List<ApproverInfo> userTaskList = new ArrayList<>();
        if(model != null){
            Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
            for(FlowElement flowElement : flowElements) {
                if(flowElement instanceof UserTask){
                    UserTask userTask = (UserTask)flowElement;
                    ApproverInfo approverInfo = new ApproverInfo();
                    approverInfo.setTaskId(userTask.getId());
                    approverInfo.setTaskName(userTask.getName());
                    String taskId = userTask.getId();
                    String[] taskArr = taskId.split("-");
                    if(StringUtils.isNotEmpty(taskArr) && taskArr.length == 2){
                        if(StringUtils.equals("single",taskArr[1])){
                            approverInfo.setApprovalType(ProcessContants.APPROVAL_TYPE_SINGLE);
                        }
                        if(StringUtils.equals("manyPeople",taskArr[1])){
                            approverInfo.setApprovalType(ProcessContants.APPROVAL_TYPE_MANGPEOPLE);
                        }
                        if(StringUtils.equals("countersign",taskArr[1])){
                            approverInfo.setApprovalType(ProcessContants.APPROVAL_TYPE_COUNTERSIGN);
                        }
                        if(StringUtils.equals("applyUser",taskArr[1])){
                            approverInfo.setApprovalType(ProcessContants.APPROVAL_TYPE_APPLYUSER);
                        }
                    }

                    ApproverInfo queryApproverInfo = approverService.queryApproverByTaskId(userTask.getId(), processDefinitionId);
                    if(queryApproverInfo != null){
                        approverInfo.setApprovalUser(queryApproverInfo.getApprovalUser());
                        approverInfo.setApprovalUserName(queryApproverInfo.getApprovalUserName());
                    }

                    userTaskList.add(approverInfo);
                }
            }
        }

        return AjaxResult.success(userTaskList);
    }

    /**
     * 读取流程图
     * @param processDefinitionId
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "/readDefinitionImg")
    public void readDefinitionImg(String processDefinitionId, HttpServletResponse response) throws Exception{
        // 设置页面不缓存
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        BpmnModel bpmnModel = this.processEngine.getRepositoryService().getBpmnModel(processDefinitionId);
        ProcessDiagramGenerator p = new DefaultProcessDiagramGenerator();

        // 获取流程图图像字符流
        //配置字体
        InputStream imageStream = p.generateDiagram(bpmnModel, "png", "宋体", "微软雅黑", "黑体", null, 2.0);

        response.setContentType("image/png");
        OutputStream os = response.getOutputStream();
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.close();
        imageStream.close();
    }

    /**
     * 查询历史审批信息
     *
     * @param instanceId
     * @return
     */
    @GetMapping("/listHistory")
    public AjaxResult listHistory(String instanceId) {
        List<HistoricActivity> list = processService.selectHistoryList(instanceId);
        return AjaxResult.success(list);
    }

    /**
     * 设置流程审批人
     * @param approverInfoVo
     * @return
     */
    @PostMapping("/addTaskApprovar")
    public AjaxResult addTaskApprovar(@RequestBody ApproverInfoVo approverInfoVo){
        approverService.addApproverList(approverInfoVo);
        return AjaxResult.success();
    }

    /**
     * 获取当前审批节点的审批选择类型，获取活动节点的连线信息
     * @param taskId
     * @return
     */
    @GetMapping("/getApprovalType")
    public AjaxResult getApprovalType(String taskId){
        List<String> outList = processService.queryApprovalType(taskId);
        return AjaxResult.success(outList);
    }

    /**
     * 查看审批流程图片
     * @param pProcessInstanceId
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "/readResource")
    public void readResource(String pProcessInstanceId, HttpServletResponse response)
            throws Exception {
        // 设置页面不缓存
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        String processDefinitionId = "";
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(pProcessInstanceId).singleResult();
        if (processInstance == null) {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(pProcessInstanceId).singleResult();
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
        } else {
            processDefinitionId = processInstance.getProcessDefinitionId();
        }
        ProcessDefinitionQuery pdq = repositoryService.createProcessDefinitionQuery();
        ProcessDefinition pd = pdq.processDefinitionId(processDefinitionId).singleResult();

        String resourceName = pd.getDiagramResourceName();

        if (resourceName.endsWith(".png") && StringUtils.isEmpty(pProcessInstanceId) == false) {
            getActivitiProccessImage(pProcessInstanceId, response);
            //ProcessDiagramGenerator.generateDiagram(pde, "png", getRuntimeService().getActiveActivityIds(processInstanceId));
        } else {
            // 通过接口读取
            InputStream resourceAsStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), resourceName);

            // 输出资源内容到相应对象
            byte[] b = new byte[1024];
            int len = -1;
            while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
                response.getOutputStream().write(b, 0, len);
            }
        }
    }

    /**
     * 获取流程图像，已执行节点和流程线高亮显示
     */
    public void getActivitiProccessImage(String pProcessInstanceId, HttpServletResponse response) {
        //logger.info("[开始]-获取流程图图像");
        try {
            //  获取历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(pProcessInstanceId).singleResult();

            if (historicProcessInstance == null) {
                //throw new BusinessException("获取流程实例ID[" + pProcessInstanceId + "]对应的历史流程实例失败！");
            } else {
                // 获取流程定义
                ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                        .getDeployedProcessDefinition(historicProcessInstance.getProcessDefinitionId());

                // 获取流程历史中已执行节点，并按照节点在流程中执行先后顺序排序
                List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(pProcessInstanceId).orderByHistoricActivityInstanceId().asc().list();

                // 已执行的节点ID集合
                List<String> executedActivityIdList = new ArrayList<String>();
                int index = 1;
                //logger.info("获取已经执行的节点ID");
                for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
                    executedActivityIdList.add(activityInstance.getActivityId());

                    //logger.info("第[" + index + "]个已执行节点=" + activityInstance.getActivityId() + " : " +activityInstance.getActivityName());
                    index++;
                }

                BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());

                // 已执行的线集合
                List<String> flowIds = new ArrayList<String>();
                // 获取流程走过的线 (getHighLightedFlows是下面的方法)
                flowIds = getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList);

                // 获取流程图图像字符流
                ProcessDiagramGenerator pec = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
                //配置字体
                InputStream imageStream = pec.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", null, 2.0);

                response.setContentType("image/png");
                OutputStream os = response.getOutputStream();
                int bytesRead = 0;
                byte[] buffer = new byte[8192];
                while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                os.close();
                imageStream.close();
            }
            //logger.info("[完成]-获取流程图图像");
        } catch (Exception e) {
            System.out.println(e.getMessage());
            //logger.error("【异常】-获取流程图失败！" + e.getMessage());
            //throw new BusinessException("获取流程图失败！" + e.getMessage());
        }
    }

    public List<String> getHighLightedFlows(BpmnModel bpmnModel, ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //24小时制
        List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId

        for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
            // 对历史流程节点进行遍历
            // 得到节点定义的详细信息
            FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());


            List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();// 用以保存后续开始时间相同的节点
            FlowNode sameActivityImpl1 = null;

            HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);// 第一个节点
            HistoricActivityInstance activityImp2_;

            for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) {
                activityImp2_ = historicActivityInstances.get(k);// 后续第1个节点

                if (activityImpl_.getActivityType().equals("userTask") && activityImp2_.getActivityType().equals("userTask") &&
                        df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime()))) //都是usertask，且主节点与后续节点的开始时间相同，说明不是真实的后继节点
                {

                } else {
                    sameActivityImpl1 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());//找到紧跟在后面的一个节点
                    break;
                }

            }
            sameStartTimeNodes.add(sameActivityImpl1); // 将后面第一个节点放在时间相同节点的集合里
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后续第一个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后续第二个节点

                if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime()))) {// 如果第一个节点和第二个节点开始时间相同保存
                    FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {// 有不相同跳出循环
                    break;
                }
            }
            List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows(); // 取出节点的所有出去的线

            for (SequenceFlow pvmTransition : pvmTransitions) {// 对所有的线进行遍历
                FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef());// 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }

        }
        return highFlows;

    }
}
